import Blocker from "./blocker";
import { DeepClone } from "./data_ext";
import Game from "./game";
import Loader from "./loader";
import Macro from "./macro";
import SerializeAble from "./serialize";
import MsgHub, { Subject, SubjectComponent } from "./subject";

class UrlParam {
    [key: string]: string;
};

export default class Utils {
    static msgHub: Subject = MsgHub;
    static macro = Macro;
    /** 入口场景 */
    static game: Game = null;
    /**
     * 顶层的遮盖，用于阻止操作。
     * @example
     *  //blocker支持3种外观
     *  Utils.type = 0; //全透明
        Utils.type = 1; //半透明黑+文字居中
        Utils.type = 2; //透明+文字居右下
    
        Utils.blocker.show(); //开启
        //做点事 
        Utils.blocker.hide(); //关闭

     */
    static blocker: Blocker = null;
    static loader: Loader = null;


    static LoadScript(src: string, id: string) {
        if (document.head.querySelector(id)) {
            return;
        }
        let sc = document.createElement("script");
        sc.setAttribute("type", "text/javascript");
        sc.id = id;
        sc.innerHTML = src;
        document.head.appendChild(sc);
    }

    static timestampToTime(timestamp: number, format: string = "") {
        if (timestamp < 9999999999) {
            timestamp *= 1000;
        }
        var date = new Date(timestamp);//时间戳为10位需*1000，时间戳为13位的话不需乘1000
        var Y = date.getFullYear();
        var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1);
        var D = date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate();
        var h = date.getHours() < 10 ? '0' + (date.getHours()) : date.getHours();
        var m = date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes();
        var s = date.getSeconds() < 10 ? '0' + (date.getSeconds()) : date.getSeconds();

        if (format) {
            //@ts-ignore
            return format.replace(/Y/, Y).replace(/M/, M).replace(/D/, D).replace(/h/, h).replace(/m/, m).replace(/s/, s);
        }
        else {
            return `${Y}年${M}月${D}日 ${h}:${m}:${s}`;
        }
    }

    /**解析url */
    static parseUrlParam(needDecodeUri = false): UrlParam {
        let url = window.location.href;
        let parse = url.substring(url.indexOf("?") + 1);
        let params = parse.split("&");
        let len = params.length;
        let item = [];
        let param = new UrlParam();
        for (let i = 0; i < len; i++) {
            item = params[i].split("=");
            if (needDecodeUri) {
                param[item[0]] = decodeURIComponent(item[1]);
            }
            else {
                param[item[0]] = item[1];
            }
        }

        return param;
    }

    static event(type: string, str: string) {
        try {
            window["_hmt"].push(["_trackEvent", type, str]);
        }
        catch (e) {
            console.log("Utils::event, info: 没有百度检测");
        }
    }

    static checkMobile(str: string) {
        // if (!(/^1[3|4|5|6|7|8][0-9]\d{4,8}$/.test(str))) {
        //     return false;
        // }
        // return true;
        return /^1[0-9]{10}$/.test(str);
    }

    // 获取远程 Bundle 包
    static loadBundle(path: string) {
        return new Promise((resolve, reject) => {
            cc.assetManager.loadBundle(path, (err, bundle: cc.AssetManager.Bundle) => {
                err ? reject(err) : resolve(bundle);
            });
        })
    }

    static getNameFromPath(path: string) {
        let pathNodes = path.split("/");
        return pathNodes[pathNodes.length - 1];
    }

    /** 截图 */
    static shot(targetNode: cc.Node, camera: cc.Camera): cc.RenderTexture {
        let node = new cc.Node();
        node.parent = targetNode;
        node.setPosition(0, 0);

        camera.enabled = false;
        // 新建一个 RenderTexture，并且设置 camera 的 targetTexture 为新建的 RenderTexture，这样 camera 的内容将会渲染到新建的 RenderTexture 中。
        let texture = new cc.RenderTexture();
        let gl = cc.game["_renderContext"];
        // 如果截图内容中不包含 Mask 组件，可以不用传递第三个参数
        texture.initWithSize(targetNode.width, targetNode.height, gl.STENCIL_INDEX8);
        camera.targetTexture = texture;

        // 渲染一次摄像机，即更新一次内容到 RenderTexture 中
        camera.render(targetNode);

        return texture;
    }
    /** 截图输出base64 */
    static shotToBase64(targetNode: cc.Node, camera: cc.Camera, type: string = "image/jpeg", flip = true, quality: number = 90): string {
        let texture = Utils.shot(targetNode, camera);
        return this.textureToBase64(texture, type, flip, quality);
    }

    static textureToBase64(texture: cc.RenderTexture, type = "image/jpeg", flip = true, quality: number = 90): string {
        // 这样我们就能从 RenderTexture 中获取到数据了
        let data = texture.readPixels();
        // 接下来就可以对这些数据进行操作了
        let canvas = document.createElement('canvas');
        let ctx = canvas.getContext('2d');
        canvas.width = texture.width;
        canvas.height = texture.height;
        let width = texture.width;
        let height = texture.height;
        if (flip) {
            let rowBytes = width * 4;
            for (let row = 0; row < height; row++) {
                let srow = height - 1 - row;
                let imageData = ctx.createImageData(width, 1);
                let start = srow * width * 4;
                for (let i = 0; i < rowBytes; i++) {
                    imageData.data[i] = data[start + i];
                }

                ctx.putImageData(imageData, 0, row);
            }
        }
        else {
            let imageData = ctx.createImageData(width, height);
            for (let i = 0; i < data.length; i++) {
                imageData.data[i] = data[i];
            }
            ctx.putImageData(imageData, 0, 0);
        }
        return canvas.toDataURL(type, quality);
    }

    static jsb_removeDir(path: string) {
        if (!jsb.fileUtils.isDirectoryExist(path)) {
            return false;
        }

        return jsb.fileUtils.removeDirectory(path);

        // let _ren = (path: string) => {
        //     let direntPaths = this.jsb_listFiles(path);
        //     direntPaths.forEach(childPath => {
        //         if (jsb.fileUtils.isFileExist(childPath)) {
        //             jsb.fileUtils.removeFile(childPath);
        //         }
        //         else if (jsb.fileUtils.isDirectoryExist(childPath)) {
        //             _ren(childPath);
        //         }
        //     });
        //     jsb.fileUtils.removeDirectory(path);
        // };

        // _ren(path);

        return true;
    }

    static jsb_listFiles(path: string) {
        if (!jsb.fileUtils.isDirectoryExist(path)) {
            return [];
        }

        let dirents = jsb.fileUtils.listFiles(path);
        return dirents.slice(2);
    }

    static base64ToTexture(base64: string) {
        return new Promise<cc.Texture2D>(ok => {
            let img = new Image();
            img.onload = () => {
                let tex = new cc.Texture2D();
                tex.initWithElement(img);
                ok(tex);
            };
            img.src = base64;
        });
    }

    static WxAppJump(targetUrl: string) {
        if (Macro.BUILD_TARGET != "res") {
            console.log("wxAppJump:", targetUrl);
        }
        else {
            try {
                //@ts-ignore
                wx.miniProgram.navigateTo({ url: targetUrl });
            } catch (e) {
                console.error(`wx.miniProgram.navigateTo({url: ${targetUrl}}) 调用失败`, e);
            }
        }
    }

    static WxAppPostMessage(data: { [key: string]: any }) {
        if (Macro.BUILD_TARGET != "res") {
            console.log("WxAppPostMessage:", data);
        }
        else {
            try {
                //@ts-ignore
                wx.miniProgram.postMessage(data);
            } catch (e) {
                console.error(`wx.miniProgram.postMessage(${data}) 调用失败`, e);
            }
        }
    }

    static NumberToDigitString(num: number, digit: number) {
        let str = num.toFixed(0);
        if (str.length < digit) {
            str = (new Array(digit - str.length).fill("0")).join("") + str;
        }
        return str;
    }
    static ChangeGroupRec(group: string, groupIndex: number, node: cc.Node) {
        node.group = group;
        node.groupIndex = groupIndex;
        node.children.forEach(child => {
            this.ChangeGroupRec(group, groupIndex, child);
        });
    }
};

export class MyMath {
    static SmoothStep(min: number, max: number, val: number) {
        let x = Math.min(1.0, Math.max(0.0, (val - min) / (max - min)));
        return x * x * (3 - 2 * x);
    }
    static Lerp(n1: number, n2: number, weight: number) {
        return n1 + (n2 - n1) * weight;
    }
    static LerpColorHex(hex1: string, hex2: string, ratio: number) {
        let c1 = cc.color(), c2 = cc.color();
        cc.Color.fromHEX(c1, hex1);
        cc.Color.fromHEX(c2, hex2);
        return this.LerpColor(c1, c2, ratio);
    }
    static LerpColor(c1: cc.Color, c2: cc.Color, ratio: number) {
        return c1.lerp(c2, ratio);
    }
    //斟茶
    // 两个数相加，第二个数有最大值限制，返回 [ 溢出值 delta, 现在目标值 ] 
    static PourValue(volume: number, volume2: number, volume2Max: number) {
        return [volume + volume2 - volume2Max, Math.min(volume + volume2, volume2Max)];
    }

    //计算二进制有多少个1
    static BitCount(bit: number) {
        let count = 0;
        while (bit) {
            count++;
            bit &= (bit - 1);
        }
        return count;
    }
};

export class Waiter<T> {
    private __awaits: { pm: Promise<T>, ok: any, reject: any }[] = [];
    await() {
        let wait: any = {};
        let pm = new Promise<T>((ok, reject) => {
            wait.ok = ok;
            wait.reject = reject;
        });
        wait.pm = pm;
        this.__awaits.push(wait);
        return pm;
    }
    finish(val: T) {
        this.__awaits.forEach(wait => {
            wait && wait.ok(val);
        });
        this.__awaits = [];
    }
    reject() {
        this.__awaits.forEach(wait => {
            wait && wait.reject();
        });
        this.__awaits = [];
    }
};

export class Sync {
    static NextUpdate(obj: any = null) {
        return new Promise<boolean>(ok => cc.director.once(cc.Director.EVENT_AFTER_UPDATE, () => {
            if (obj !== null) {
                ok(cc.isValid(obj));
            }
            else {
                ok(true);
            }
        }));
    }
    static DelayTime(time: number, obj: any = null) {
        return new Promise<boolean>(ok => setTimeout(() => {
            if (obj !== null) {
                ok(cc.isValid(obj));
            }
            else {
                ok(true);
            }
        }, time * 1000));
    }
    static AnimationEnd(aniName: string, ani: cc.Animation) {
        return new Promise<boolean>(ok => {
            let _ok = () => {
                ani.targetOff(this);
                ok(cc.isValid(ani));
            };
            ani.once(cc.Animation.EventType.FINISHED, _ok, this);
            ani.once(cc.Animation.EventType.STOP, _ok, this);
            ani.play(aniName);
        });
    }
    static SubjectMessage<T>(eventName: string | { new(): T }, sub: Subject | SubjectComponent) {
        if (typeof eventName !== "string") {
            eventName = cc.js.getClassName(eventName) || eventName.name;
        }
        return new Promise<T>(ok => sub.once(eventName, ok, this));
    }
    //会调用cc.Tween.start
    static TweenEnd(tween: cc.Tween) {
        return new Promise<boolean>(ok => tween.call(() => {
            //@ts-ignore
            ok(cc.isValid(tween._target));
        }).start());
    }
};

//贝塞尔曲线
export class Bezier {
    //贝塞尔曲线
    static bezierLine(pts: cc.Vec2[], step: number) {
        let bezierPts = [];
        let ppc = 1 / step;
        let percent = 0;
        bezierPts.push(this.bezierPoint(pts, 0));
        for (let i = 0; i < step; i++) {
            percent += ppc;
            bezierPts.push(this.bezierPoint(pts, percent));
        }

        return bezierPts;
    }
    //贝塞尔曲线 某点
    static bezierPoint(pts: cc.Vec2[], t: number) {
        pts = pts.slice(0, 4);
        if (pts.length == 3) {
            // P = (1−t) 2P1 + 2(1−t)tP2 + t2P3
            return cc.v2(
                Math.pow(1 - t, 2) * pts[0].x + (2 * (1 - t) * t * pts[1].x) + Math.pow(t, 2) * pts[2].x,
                Math.pow(1 - t, 2) * pts[0].y + (2 * (1 - t) * t * pts[1].y) + Math.pow(t, 2) * pts[2].y
            );
        }
        else if (pts.length == 4) {
            // P = (1−t) 3(P1) + 3(1−t)2t(P2) + 3(1−t)t2(P3) + t3(P4)
            return cc.v2(
                Math.pow(1 - t, 3) * pts[0].x + (3 * t * Math.pow(t - 1, 2) * pts[1].x) + (3 * Math.pow(t, 2) * (1 - t) * pts[2].x) + Math.pow(t, 3) * pts[3].x,
                Math.pow(1 - t, 3) * pts[0].y + (3 * t * Math.pow(t - 1, 2) * pts[1].y) + (3 * Math.pow(t, 2) * (1 - t) * pts[2].y) + Math.pow(t, 3) * pts[3].y,
            );
        }
    }
};

import { uid } from "uid";
/** 获取随机字符串，最大21位。 */
export class Uid {
    static getUid(digit = 15) {
        digit = Math.min(21, Math.max(11, digit));
        return uid(digit);
    }
};


export class ArrayUtils {
    static ConcatUnique(...arrs: Array<any>[]) {
        let sum = [];
        arrs.forEach(arr => {
            sum = sum.concat(arr);
        });
        return Array.from(new Set(sum));
    }
    static Fill<T>(count: number, def: T) {
        let arr: T[] = [];
        for (let i = 0; i < count; i++) {
            if (def) {
                arr.push(DeepClone(def));
            }
            else {
                arr.push(null);
            }
        }
        return arr;
    }
    static Compare<T>(arrA: T[], arrB: T[]) {
        let adds = [], dels = [];

        for (let a = 0; a < arrA.length; a++) {
            let ea = arrA[a];
            if (!arrB.find(ele => ele === ea)) {
                dels.push(ea);
            }
        }

        for (let b = 0; b < arrB.length; b++) {
            let eb = arrB[b];
            if (!arrA.find(ele => ele === eb)) {
                adds.push(eb);
            }
        }

        return [adds, dels];
    }
};

/** 随机 */
export class Random {
    /**范围 Floor */
    static range(min, max) {
        return Math.floor(Math.random() * (max - min) + min);
    }
    /**范围 四舍五入 */
    static rangeRound(min, max) {
        return Math.round(Math.random() * (max - min) + min);
    }
    /**范围 Ceil */
    static rangeCeil(min, max) {
        return Math.ceil(Math.random() * (max - min) + min);
    }
    static bool(percent) {
        return (Math.random() < percent);
    }
    static dice(faceCount: number) {
        return Math.floor(Math.random() * faceCount) + 1;
    }
    static dices(faceCount: number, diceCount = 1) {
        let res: number[] = [];
        for (let i = 0; i < diceCount; i++) {
            res.push(this.dice(faceCount));
        }
        return res;
    }
    /** 
     * 选择器
     * @example
     * Random.selecter( 1,2,3,4 );
     * let arr = [ 1,2,3,4 ];
     * Random.selecter( [1,2,3,4] );
     */
    static selecter(list: any[]) {
        return list[Random.range(0, list.length)];
    }

    static splitValue(total: number, count: number, diffRange: number = 0) {

        let yu = total % count;
        let iVal = total - yu;
        let avg = iVal / count;

        let sum = 0;
        let arr = [];
        for (let i = 0; i < count; i++) {
            let dt = Math.round((Math.random() > 0.5 ? -1 : 1) * Math.random() * diffRange);
            let val = avg + dt;

            if (yu > 0) {
                yu--;
                val++;
            }

            arr.push(val);
            sum += val;
        }

        if (sum != total) {
            let totalDt = sum - total;
            let yuDt = totalDt % count;
            let iValDt = totalDt - yuDt;
            let avgDt = (iValDt != 0 ? iValDt / count : 0);
            for (let i = 0; i < arr.length; i++) {
                arr[i] -= avgDt;
                if (yuDt > 0) {
                    yuDt--;
                    arr[i]--;
                }
                else if (yuDt < 0) {
                    yuDt++;
                    arr[i]++;
                }
            }
        }

        return arr;
    }
};


//顺序抽取
export class DrawOrder {
    arr: any = [];
    isLoop: boolean = false;
    isReverse: boolean = false;
    index = 0;
    /**
     * @param arr 列表
     * @param isLoop 循环
     * @param isReverse 倒序
     */
    constructor(arr, isLoop = false, isReverse = false) {
        this.isLoop = isLoop;
        this.isReverse = isReverse;
        this.reset(arr);
    }
    /** 重置 */
    reset(arr) {
        this.index = 0;
        this.arr = arr.slice();
    }
    /** 终止 */
    end() {
        if (this.isLoop) {
            return false;
        }

        if (this.isReverse) {
            this.index < 0;
        }
        else {
            this.index > this.arr.length - 1;
        }
    }
    /** 从列表中抽取一个，然后索引+1 */
    pick() {
        if (this.end()) {
            return null;
        }
        let out = null;
        if (this.isReverse) {
            this.index--;
            if (this.index < 0) {
                this.index = this.arr.length - 1;
            }
            out = this.arr[this.index];
        }
        else {
            this.index++;
            if (this.index > this.arr.length - 1) {
                this.index = 0;
            }
            out = this.arr[this.index];
        }
        return out;
    }
};

//随机抽取
export class DrawRandom {
    _map: Map<number, any> = null;
    _arr: any[] = null;
    constructor(arr: any[]) {
        this.reset(arr);
    }
    /** 重置 */
    reset(arr: any[]) {
        this._map = new Map();
        this._arr = arr.slice();
        for (let i = 0; i < arr.length; i++) {
            this._map.set(i, arr[i]);
        }
    }
    /* 检查空 */
    empty() {
        return this._map.size == 0
    }
    protected _keys() {
        let keys = [];
        this._map.forEach((value, key) => {
            keys.push(key);
        });
        return keys;
    }
    /** 从列表中抽取一个, 去除*/
    pick() {
        if (!this.empty()) {
            let keyList = this._keys();
            let ind = keyList[Random.range(0, keyList.length)];
            let val = this._map.get(ind);
            this._map.delete(ind);
            return val;
        }
        else {
            return null;
        }
    }
    /** 从列表中抽取一个, 去除, 输出index */
    pickIndex() {
        if (!this.empty()) {
            let keyList = this._keys();
            let ind = keyList[Random.range(0, keyList.length)];
            this._map.delete(ind);
            return ind;
        }
        else {
            return null;
        }
    }
};


/** 假随机
 * @example
 * let fr = new FakeRandom( 0, 4 );
 * console.log( fr.step ); //  1/4的几率返回true
 * console.log( fr.step ); //  2/4的几率返回true
 * console.log( fr.step ); //  3/4的几率返回true
 * console.log( fr.step ); //  一定返回true
 * console.log( fr.step ); //  1/4的几率返回true
 * console.log( fr.step ); //  2/4的几率返回true，如果此次返回true，则重置。
 * console.log( fr.step ); //  1/4的几率返回true
 */
export class FakeRandom {
    private __trueStep = 0;
    protected _step = 0;
    maxStep: number = 0;
    minStep: number = 0;
    loop: boolean = true;
    resetOnTrue: boolean = true;
    /**
     * @param minStep 达到这个数字前，一定返回false
     * @param maxStep 达到这个数字后，一定返回true
     * @param resetOnTrue 是否在返回true后重置
     * @param loop 循环
     */
    constructor(minStep: number, maxStep: number, resetOnTrue = true, loop = true) {
        this.maxStep = maxStep;
        this.minStep = minStep;
        this.loop = loop;
        this.resetOnTrue = resetOnTrue;
    }
    get step() {
        this._step++;
        if (this._step > this.maxStep) {
            if (this.loop) {
                this._step = 0;
                this.__trueStep = 0;
            }
            else {
                this._step = this.maxStep;
            }
        }

        let b = this.__trueStep > 0 ? false : this.rand();

        if (b) {
            this.__trueStep++;
            if (this.resetOnTrue) {
                this._step = 0;
                this.__trueStep = 0;
            }
        }
        console.log("FakeRandom::step", this._step, this.maxStep, b);
        return b;
    }
    rand() {
        if (this._step < this.minStep) {
            return false;
        }
        else {
            return Math.floor(Math.random() * this.maxStep) < this._step;
        }
    }
};

// 1、Kp是比例调节系数，在PID调节器中起到加快系统的响应速度，提高系统的调节精度，快速调节误差的作用。

// 2、Ki是积分调节系数，在PID调节器中起到消除残差，调节稳态时间的作用。

// 3、Kd是微分调节系数，在PID调节器中起到改善系统的动态性能，预测误差趋势，提前修正误差的作用。
export class PID {
    max: number = 0;
    min: number = 0;
    Kp: number = 0;
    Ki: number = 0;
    Kd: number = 0;

    // protected _pre_error: number = 0;
    // protected _integral: number = 0;
    pre_error: number = 0;
    integral: number = 0;

    constructor(max: number, min: number, Kp: number, Kd: number, Ki: number) {
        this.max = max;
        this.min = min;
        this.Kp = Kp;
        this.Ki = Ki;
        this.Kd = Kd;
    }

    calculate(dt: number, target: number, pv: number) { //输出修正值delta
        // Calculate error
        let error = target - pv;

        // Proportional term
        let Pout = this.Kp * error;

        // Integral term
        this.integral += error * dt;
        let Iout = this.Ki * this.integral;

        // Derivative term
        let derivative = (error - this.pre_error) / dt;
        let Dout = this.Kd * derivative;

        // Calculate total output
        let delta = Pout + Iout + Dout;

        // Restrict to max/min
        if (delta > this.max)
            delta = this.max;
        else if (delta < this.min)
            delta = this.min;

        // Save error to previous error
        this.pre_error = error;

        return delta;
    }
};

export class PIDController {
    Kp: number = 0.15; // 比例系数
    Ki: number = 0.15; // 积分系数
    Kd: number = 0.04; // 微分系数

    setPoint: number = 0; // 目标值
    lastError: number = 0; // 上一次的误差
    integral: number = 0; // 积分项

    public reset(Kp = 0.15, Ki = 0.15, Kd = 0.04, target: number = undefined) {
        this.Kp = Kp;
        this.Ki = Ki;
        this.Kd = Kd;
        if (target !== undefined) {
            this.setPoint = target;
        }
        this.lastError = 0;
        this.integral = 0;
    }

    public setTarget(value: number): void {
        this.setPoint = value;
    }

    public update(currentValue: number): number {
        let error: number = this.setPoint - currentValue; // 计算误差
        let pTerm: number = this.Kp * error; // 计算P项
        this.integral += error; // 更新I项的积分
        let iTerm: number = this.Ki * this.integral;
        let derivative: number = error - this.lastError; // 计算D项的导数
        let dTerm: number = this.Kd * derivative;

        // 计算PID输出
        let output: number = pTerm + iTerm + dTerm;

        // 更新上一次的误差为当前误差
        this.lastError = error;

        return output;
    }
}



export class GraphicsHelper {
    static DrawPoly(drawer: cc.Graphics, pts: cc.Vec2[], isSeal: boolean = true) {
        let p0 = pts[0];
        drawer.moveTo(p0.x, p0.y);
        for (let i = 1; i < pts.length; i++) {
            let pt = pts[i];
            drawer.lineTo(pt.x, pt.y);
        }
        if (isSeal) {
            drawer.lineTo(p0.x, p0.y);
        }
    }
};

export class VectorHelper {
    static getNearestNodeIndex(v2: cc.Vec2, nodes: cc.Node[], range: number = Infinity) {
        let selectInd = -1;
        let edge = range;
        nodes.forEach((child, ind) => {
            let dis = cc.v2(child.position).sub(v2).mag();
            if (edge > dis) {
                edge = dis;
                selectInd = ind;
            }
        });

        return selectInd;
    }
    static getNearestNode(v2: cc.Vec2, nodes: cc.Node[], range: number = Infinity) {
        let selectIndex = VectorHelper.getNearestNodeIndex(v2, nodes, range);
        if (selectIndex !== -1) {
            return nodes[selectIndex];
        }
        else {
            return null;
        }
    }

    static getFarestNodeIndex(v2: cc.Vec2, nodes: cc.Node[], range: number = -Infinity) {
        let selectInd = -1;
        let edge = range;
        nodes.forEach((child, ind) => {
            let dis = cc.v2(child.position).sub(v2).mag();
            if (edge < dis) {
                edge = dis;
                selectInd = ind;
            }
        });

        return selectInd;
    }
    static getFarestNode(v2: cc.Vec2, nodes: cc.Node[], range: number = -Infinity) {
        let selectIndex = VectorHelper.getFarestNodeIndex(v2, nodes, range);
        if (selectIndex !== -1) {
            return nodes[selectIndex];
        }
        else {
            return null;
        }
    }
};
window["utils"] = Utils;